Introduction

Food prices play an important role in the overall Consumer Price Index (CPI) calculations. Considering the CPI basket used to calculate inflation rate or CPI, proportion of food products is important. In addition, CPI is an overall generic calculation about price levels and it may not be relevant for all households. That’s simply because for low income families, the proportion of food related spendings is higher compared to mid or high income families. Therefore, food price index deserves a special interest.

In this analysis, I wanted to analyze characteristics of food price index (FPI) of Canada and build a model to forecast food price index values for 2023. This analysis is a simple time series analysis which only considers past values of food price index. In the following parts

Model Fitting

1. Required Packages

library(statcanR)
library(dplyr)
library(ggplot2)
library(magrittr)
library(forecast)
library(tseries)
library(ggTimeSeries)
library(stringr)
library(stats)
library(gridExtra)
library(FinTS)
library(nortsTest)
library(MTS)
library(TSstudio)

2. Data Collection from Statistics Canada

2.1. Data License

The data used in the analysis is directly imported from Statistics Canada with an open license letting users to use, publish and freely distribute the dataset.

2.2. Data Sharing API

Statistics Canada has an API for data sharing and using the R package statcanR, data can be directly retrieved from StatCan by only using table id. The imported data is in normalized database table format and therefore there is no need to data transformation or wrangling.

2.3. Data Collection

Statistics Canada has Consumer Price Index, monthly, seasonally adjusted dataset, which contains overall CPI and price indexes for sub-categories like food. This dataset can be accessed with the table id = 18-10-0006-01.

df = statcan_data('18-10-0006-01', 'eng')
names(df) = str_replace_all(names(df), c(" " = "_"))
fpi = df %>% 
  filter(Products_and_product_groups == 'Food') %>% 
  select(c(REF_DATE, VALUE))
fpi = ts(fpi$VALUE, start=c(1992,1), frequency = 12)
rm(df)

3. Data Visualization

plot.ts(fpi,
        main = 'Food Price Index of Canada',
        sub = 'From 1992 to Present',
        col = 'darkred',
        xy.lines = TRUE,
        xlab = 'Time',
        ylab = 'Index Value',
        lwd = 3)
abline(reg=lm(fpi~time(fpi)), col = 'darkgrey', lwd = 2)

Above plot exhibits and obvious increasing trend for FPI, which can be seen with the fitted grey linear trend line. In addition, the changes in FPI are not constant. For some periods, the line is flatter compared to drastic increase period like 2021 and 2022.

This dataset is seasonally adjusted but in the following part, I tried to decompose the FPI and get trend and seasonal components together with the random (residual) part.

4. Decomposition of CPI

decomposed_fpi <- tslm(fpi ~ trend + fourier(fpi, 2))

trend <- coef(decomposed_fpi)[1] + coef(decomposed_fpi)['trend']*seq_along(fpi)

components <- cbind(
  FPI = fpi,
  trend = trend,
  seasonal = fpi - trend - residuals(decomposed_fpi),
  residuals = residuals(decomposed_fpi))
autoplot(components, facet=TRUE)

There are 4 subplots in the above visual and the first one is the sum of the last three meaning that FPI has a trend, a seasonal and an unexplained residual component. This is interesting because I would not expect any seasonal component since the data was seasonally adjusted.

Another interesting result is that we cannot only explain the variation in this time series with trend and seasonality: residuals seem to be non-random and for some periods, they appear to be autocorrelated.

5. Distribution of Food Price Index

5.1. Visuals

hist(fpi,
     freq = FALSE,
     main = 'Histogram of Food Price Index',
     xlab = 'Food Price Index',
     col = 'darkred')

The distribution of FPI (as is) is not even close to normal distribution.

hist(log(fpi),
     freq = FALSE,
     main = 'Histogram of log(Food Price Index)',
     xlab = 'log(Food Price Index)',
     col = 'darkgrey')

Above visual is the distribution of natural logartihm of FPI and even the log transformed version is not normally distributed.

hist(diff(log(fpi)),
     freq = FALSE,
     main = 'Histogram of diff(log(Food Price Index))',
     xlab = 'diff(log(Food Price Index))',
     col = 'darkgrey')

In addition to the log transformation, the monthly differences in FPI were calculated and plotted in above visual. The distribution shape of the log differenced FPI is better than the former two trials and very close to normal.

5.2. Statistical Tests for Normality

Although visuals play great role in understanding the distribution of time series, formally statistical tests are required to check the normality of the variables. In the following part, I used Jarque-Bera Test to statistically check whether the time series is normally distributed or not. In these statistical tests, following hypotheses were used:

\[ \begin{align*} H_0: & \text{The distribution of FPI IS NOT statistically different than normal distribution} \\ H_A: & \text{The distribution of FPI IS statistically different than normal distribution} \end{align*} \]

jarque.bera.test(fpi)

    Jarque Bera Test

data:  fpi
X-squared = 24.796, df = 2, p-value = 4.128e-06
jarque.bera.test(log(fpi))

    Jarque Bera Test

data:  log(fpi)
X-squared = 26.026, df = 2, p-value = 2.231e-06
jarque.bera.test(diff(log(fpi)))

    Jarque Bera Test

data:  diff(log(fpi))
X-squared = 32.618, df = 2, p-value = 8.26e-08

FPI, log(FPI) and diff(log(FPI)) were tested and all tests resulted in \(p-value < \alpha = 0.05\) meaning the rejection of \(H_0\), which states normal distribution of FPI. Therefore, the conclusion is that the time series is not normally distributed.

6. Testing Stationary (White Noise)

6.1. Visuals

6.1.1. Lag Plot to Check Autocorrelation

gglagplot(x = log(fpi), lag=12, seasonal = TRUE) + 
  scale_color_viridis_d(option = "A") +
  theme_bw()

The scatterplot grid presented above indicates that log(FPI) is significantly correlated with its lags.

gglagplot(x = diff(log(fpi)), lag=12, seasonal = TRUE) + 
  scale_color_viridis_d(option = "A") +
  theme_bw()

Instead of log(FPI), I used diff(log(FPI)) to check for autocorrelation. This time, the log differenced FPI showed no significant autocorrelation with lag values.

6.1.2. Autocorrelation and Partial Autocorrelation Plots (ACF & PACF)

fpi_acf = ggAcf(log(fpi), lag.max = 50, plot = TRUE) + 
  labs(title = "log(FPI) ACF")+
  theme_classic()
fpi_pacf = ggPacf(log(fpi), lag.max = 50, plot = TRUE) +
  labs(title = "log(FPI) PACF")+
  theme_classic()
grid.arrange(fpi_acf, fpi_pacf, nrow = 1)

ACF and PACF are important visuals to check while fitting time series models. Considering the plotted ACF and PACF plotted above, I can state that log(FPI) follows a MA process instead of an AR process since bars in ACF slightly decreases with increasing lags but bars in PACF directly becomes insignificant after first one.

6.2. Statistical Tests for Stationary (White Noise)

Similar to the normality test, in addition to the visuals, I used statistical test to detect stationary (unit root). Following hypotheses were used in these tests.

\[ \begin{align*} H_0: & \text{Non Stationary (Unit Root present)} \\ H_A: & \text{Stationary (Unit Root NOT present)} \end{align*} \]

adf.test(log(fpi), alternative = 'stationary')

    Augmented Dickey-Fuller Test

data:  log(fpi)
Dickey-Fuller = -2.2, Lag order = 7, p-value = 0.4926
alternative hypothesis: stationary
adf.test(diff(log(fpi)), alternative = 'stationary')
Warning: p-value smaller than printed p-value

    Augmented Dickey-Fuller Test

data:  diff(log(fpi))
Dickey-Fuller = -5.1073, Lag order = 7, p-value = 0.01
alternative hypothesis: stationary

Statistical test results supports the visuals discussed in the previous part:

  • For log(FPI), the calculated \(p-value > \alpha = 0.05\) meaning that \(H_0\) cannot be rejected. The conclusion for log(FPI) is that it has unit root (or it is not stationary). This is not surprising since the original FPI data had a trend component (refer to decomposition part discussed above).

  • For log differenced FPI, my conclusion is the opposite since the calculated \(p-value < \alpha = 0.05\) meaning that \(H_0\) must be be rejected. The conclusion for diff(log(FPI)) is that it does not have unit root (or it is stationary).

In above part, I discussed about FPI (and log(FPI)) follow a MA process instead of an AR process. In addition to that, statistical test results discussed in this part also suggest that there will be an I component in the ARIMA process because with the integrated differencing, data become stationary.

7. ARIMA (or SARIMA) Model

7.1. Fitting the model

A general ARIMA model requires three parameters:

  • p for AR component: p refers to number of lag values to include in the model

  • d for I component: d refers to differencing window

  • q for MA component: q refers to the number of past residuals to include in the model.

Remember, in the decomposition part, I discussed about the surprising seasonal component of FPI. Therefore, my expectation is that the fitted model will be SARIMA (seasonal ARIMA), which requires three additional parameters (P,D,Q same meaning with above parameters but this time seasonal).

The optimal parameters can be decided with trials and then comparing the AIC or BIC values (the lowest should be chosen) of the models. However, auto.arima() function exactly does the same thing and therefore I prefer to use it to be practical.

Note: Since differencing can be handled within the ARIMA (or SARIMA) process by setting d>0, I used log(FPI) inside the function. auto.arima will choose a non-zero value for d to manage differencing.

ARIMA_fpi = auto.arima(log(fpi))
summary(ARIMA_fpi)
Series: log(fpi) 
ARIMA(0,2,1)(0,0,2)[12] 

Coefficients:
          ma1     sma1     sma2
      -0.9022  -0.2051  -0.2649
s.e.   0.0287   0.0526   0.0502

sigma^2 = 1.224e-05:  log likelihood = 1568.27
AIC=-3128.54   AICc=-3128.43   BIC=-3112.89

Training set error measures:
                       ME        RMSE         MAE         MPE       MAPE      MASE        ACF1
Training set 0.0001044633 0.003475398 0.002661481 0.001965791 0.05641366 0.1075394 -0.04868011

Similar to my discussion in former sections of this paper, auto.arima() function resulted in a SARIMA(0,2,1)(0,0,1) model. As discussed above,

  • There is no AR component

  • There is I component with d = 2

  • There is MA component with q = 1

  • There is seasonal MA with Q = 1

8. Model Diagnostics

tsdiag(ARIMA_fpi)

The residual plot of the fitted model indicates signs of non-constant variance since there are picks in the residuals in some periods compared to other periods. This shows the variation in the residuals are not constant.

checkresiduals(ARIMA_fpi, test = FALSE)

The ACF of residuals look acceptable and the distribution of residuals is close to normal distribution.

autoplot(ARIMA_fpi)

All the dots are inside the circle, which is an indication of an acceptable model.

archTest(residuals(ARIMA_fpi))
Q(m) of squared series(LM test):  
Test statistic:  18.60339  p-value:  0.04559924 
Rank-based Test:  
Test statistic:  17.58444  p-value:  0.0623914 

In order to test for heteroscedasticity, I used archTest from MTS package (which uses Ljung-Box test). The \(p-value\) of the rank-based test is greater than \(\alpha = 0.05\) but the normal test has a \(p-value < \alpha = 0.05\).
\[ \begin{align*} H_0: & \text{No autocorrelation of residual squares (Residuals are random)} \\ H_A: & \text{Autocorrelation of residual squares (Residuals are NOT random)} \end{align*} \]

According to the package definition for archTest(), the rank series of the squared time series is than used to test the conditional heteroscedasticity. Our model is successful in terms of rank-based test model and therefore, we can make forecasts based on the existing model. However, the p-values are very close to the critical levels and therefore, there is a need for ARCH or GARCH models to predict the variance.

Forecasting

fpi_forecast = forecast(ARIMA_fpi, h = 12, level = c(95,99))

fpi_forecast$x = exp(fpi_forecast$x)
fpi_forecast$mean = exp(fpi_forecast$mean)
fpi_forecast$lower = exp(fpi_forecast$lower)
fpi_forecast$upper = exp(fpi_forecast$upper)

# using plotly in R to plot interactive visuals. 
plot_forecast(fpi_forecast, 
              title = 'Food Price Index Forecast for 2023', 
              Xtitle = 'Time', 
              Ytitle = 'Food Price Index', 
              color = NULL, 
              width = 2)

Conclusion

Canada’s CPI for food (or FPI) follows a seasonal ARIMA process. Following the tests implemented to ensure meeting the assumptions, I fitted a seasonal ARIMA model to forecast FPI values for 2023. My forecasts indicates that food prices in Canada are likely to increase further in 2023. Furthermore, I plan to fit an ARCH or GARCH model for the conditional heteroscedasticity as the next step of this paper.

References

Roser, Max, and Hannah Ritchie. (2021) Food Prices. Our World in Data, ourworldindata.org. Available at: https://ourworldindata.org/food-prices. (Accessed: January 18, 2023).

Bogmans, C., Pescatori, A. and Prifti, E. (2021) Four facts about soaring consumer food prices, IMF. Available at: https://www.imf.org/en/Blogs/Articles/2021/06/24/four-facts-about-soaring-consumer-food-prices (Accessed: January 18, 2023).

Fradella, A. (2022) Behind the Numbers: What’s Causing Growth in Food Prices, Government of Canada, Statistics Canada. Available at: https://www150.statcan.gc.ca/n1/pub/62f0014m/62f0014m2022014-eng.htm (Accessed: January 18, 2023).

Statistics Canada (2023) Consumer price index, monthly, seasonally adjusted, Consumer Price Index, monthly, seasonally adjusted. Government of Canada, Statistics Canada. Available at: https://www150.statcan.gc.ca/t1/tbl1/en/tv.action?pid=1810000601 (Accessed: January 18, 2023).

Statistics Canada (2022) Statistics Canada Open Licence, Government of Canada, Statistics Canada. Government of Canada, Statistics Canada. Available at: https://www.statcan.gc.ca/en/reference/licence (Accessed: January 18, 2023).

Statistics Canada (2022) Web data service (WDS), Government of Canada, Statistics Canada. Government of Canada, Statistics Canada. Available at: https://www.statcan.gc.ca/en/developers/wds (Accessed: January 18, 2023).

LS0tDQp0aXRsZTogIkZvb2QgUHJpY2UgSW5kZXggb2YgQ2FuYWRhIg0Kc3VidGl0bGU6ICJVbml2YXJpYXRlIFRpbWUgU2VyaWVzIEFuYWx5c2lzIHRvIGZvcmVjYXN0IGZ1dHVyZSBGb29kIFByaWNlIEluZGV4IHZhbHVlcyINCmF1dGhvcjogIk8uIFRheWxhbiBDaWNlaywgVGhlIFVuaXZlcnNpdHkgb2YgQ2FsZ2FyeSINCmRhdGU6IEphbiAxOCwgMjAyMw0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24NCg0KRm9vZCBwcmljZXMgcGxheSBhbiBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgb3ZlcmFsbCBDb25zdW1lciBQcmljZSBJbmRleCAoQ1BJKSBjYWxjdWxhdGlvbnMuIENvbnNpZGVyaW5nIHRoZSBDUEkgYmFza2V0IHVzZWQgdG8gY2FsY3VsYXRlIGluZmxhdGlvbiByYXRlIG9yIENQSSwgcHJvcG9ydGlvbiBvZiBmb29kIHByb2R1Y3RzIGlzIGltcG9ydGFudC4gSW4gYWRkaXRpb24sIENQSSBpcyBhbiBvdmVyYWxsIGdlbmVyaWMgY2FsY3VsYXRpb24gYWJvdXQgcHJpY2UgbGV2ZWxzIGFuZCBpdCBtYXkgbm90IGJlIHJlbGV2YW50IGZvciBhbGwgaG91c2Vob2xkcy4gVGhhdCdzIHNpbXBseSBiZWNhdXNlIGZvciBsb3cgaW5jb21lIGZhbWlsaWVzLCB0aGUgcHJvcG9ydGlvbiBvZiBmb29kIHJlbGF0ZWQgc3BlbmRpbmdzIGlzIGhpZ2hlciBjb21wYXJlZCB0byBtaWQgb3IgaGlnaCBpbmNvbWUgZmFtaWxpZXMuIFRoZXJlZm9yZSwgZm9vZCBwcmljZSBpbmRleCBkZXNlcnZlcyBhIHNwZWNpYWwgaW50ZXJlc3QuDQoNCkluIHRoaXMgYW5hbHlzaXMsIEkgd2FudGVkIHRvIGFuYWx5emUgY2hhcmFjdGVyaXN0aWNzIG9mIGZvb2QgcHJpY2UgaW5kZXggKEZQSSkgb2YgQ2FuYWRhIGFuZCBidWlsZCBhIG1vZGVsIHRvIGZvcmVjYXN0IGZvb2QgcHJpY2UgaW5kZXggdmFsdWVzIGZvciAyMDIzLiBUaGlzIGFuYWx5c2lzIGlzIGEgc2ltcGxlIHRpbWUgc2VyaWVzIGFuYWx5c2lzIHdoaWNoIG9ubHkgY29uc2lkZXJzIHBhc3QgdmFsdWVzIG9mIGZvb2QgcHJpY2UgaW5kZXguIEluIHRoZSBmb2xsb3dpbmcgcGFydHMNCg0KIyBNb2RlbCBGaXR0aW5nDQoNCiMjIDEuIFJlcXVpcmVkIFBhY2thZ2VzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHN0YXRjYW5SKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0c2VyaWVzKQ0KbGlicmFyeShnZ1RpbWVTZXJpZXMpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHN0YXRzKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KEZpblRTKQ0KbGlicmFyeShub3J0c1Rlc3QpDQpsaWJyYXJ5KE1UUykNCmxpYnJhcnkoVFNzdHVkaW8pDQpgYGANCg0KIyMgMi4gRGF0YSBDb2xsZWN0aW9uIGZyb20gU3RhdGlzdGljcyBDYW5hZGENCg0KIyMjIDIuMS4gRGF0YSBMaWNlbnNlDQoNClRoZSBkYXRhIHVzZWQgaW4gdGhlIGFuYWx5c2lzIGlzIGRpcmVjdGx5IGltcG9ydGVkIGZyb20gU3RhdGlzdGljcyBDYW5hZGEgd2l0aCBhbiBvcGVuIGxpY2Vuc2UgbGV0dGluZyB1c2VycyB0byB1c2UsIHB1Ymxpc2ggYW5kIGZyZWVseSBkaXN0cmlidXRlIHRoZSBkYXRhc2V0Lg0KDQojIyMgMi4yLiBEYXRhIFNoYXJpbmcgQVBJDQoNClN0YXRpc3RpY3MgQ2FuYWRhIGhhcyBhbiBBUEkgZm9yIGRhdGEgc2hhcmluZyBhbmQgdXNpbmcgdGhlIFIgcGFja2FnZSAqKnN0YXRjYW5SKiosIGRhdGEgY2FuIGJlIGRpcmVjdGx5IHJldHJpZXZlZCBmcm9tIFN0YXRDYW4gYnkgb25seSB1c2luZyB0YWJsZSBpZC4gVGhlIGltcG9ydGVkIGRhdGEgaXMgaW4gbm9ybWFsaXplZCBkYXRhYmFzZSB0YWJsZSBmb3JtYXQgYW5kIHRoZXJlZm9yZSB0aGVyZSBpcyBubyBuZWVkIHRvIGRhdGEgdHJhbnNmb3JtYXRpb24gb3Igd3JhbmdsaW5nLg0KDQojIyMgMi4zLiBEYXRhIENvbGxlY3Rpb24NCg0KU3RhdGlzdGljcyBDYW5hZGEgaGFzICoqKkNvbnN1bWVyIFByaWNlIEluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKioqIGRhdGFzZXQsIHdoaWNoIGNvbnRhaW5zIG92ZXJhbGwgQ1BJIGFuZCBwcmljZSBpbmRleGVzIGZvciBzdWItY2F0ZWdvcmllcyBsaWtlIGZvb2QuIFRoaXMgZGF0YXNldCBjYW4gYmUgYWNjZXNzZWQgd2l0aCB0aGUgdGFibGUgaWQgPSAxOC0xMC0wMDA2LTAxLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZGYgPSBzdGF0Y2FuX2RhdGEoJzE4LTEwLTAwMDYtMDEnLCAnZW5nJykNCm5hbWVzKGRmKSA9IHN0cl9yZXBsYWNlX2FsbChuYW1lcyhkZiksIGMoIiAiID0gIl8iKSkNCmZwaSA9IGRmICU+JSANCiAgZmlsdGVyKFByb2R1Y3RzX2FuZF9wcm9kdWN0X2dyb3VwcyA9PSAnRm9vZCcpICU+JSANCiAgc2VsZWN0KGMoUkVGX0RBVEUsIFZBTFVFKSkNCmZwaSA9IHRzKGZwaSRWQUxVRSwgc3RhcnQ9YygxOTkyLDEpLCBmcmVxdWVuY3kgPSAxMikNCnJtKGRmKQ0KYGBgDQoNCiMjIDMuIERhdGEgVmlzdWFsaXphdGlvbg0KDQpgYGB7cn0NCnBsb3QudHMoZnBpLA0KICAgICAgICBtYWluID0gJ0Zvb2QgUHJpY2UgSW5kZXggb2YgQ2FuYWRhJywNCiAgICAgICAgc3ViID0gJ0Zyb20gMTk5MiB0byBQcmVzZW50JywNCiAgICAgICAgY29sID0gJ2RhcmtyZWQnLA0KICAgICAgICB4eS5saW5lcyA9IFRSVUUsDQogICAgICAgIHhsYWIgPSAnVGltZScsDQogICAgICAgIHlsYWIgPSAnSW5kZXggVmFsdWUnLA0KICAgICAgICBsd2QgPSAzKQ0KYWJsaW5lKHJlZz1sbShmcGl+dGltZShmcGkpKSwgY29sID0gJ2RhcmtncmV5JywgbHdkID0gMikNCmBgYA0KDQpBYm92ZSBwbG90IGV4aGliaXRzIGFuZCBvYnZpb3VzIGluY3JlYXNpbmcgdHJlbmQgZm9yIEZQSSwgd2hpY2ggY2FuIGJlIHNlZW4gd2l0aCB0aGUgZml0dGVkIGdyZXkgbGluZWFyIHRyZW5kIGxpbmUuIEluIGFkZGl0aW9uLCB0aGUgY2hhbmdlcyBpbiBGUEkgYXJlIG5vdCBjb25zdGFudC4gRm9yIHNvbWUgcGVyaW9kcywgdGhlIGxpbmUgaXMgZmxhdHRlciBjb21wYXJlZCB0byBkcmFzdGljIGluY3JlYXNlIHBlcmlvZCBsaWtlIDIwMjEgYW5kIDIwMjIuDQoNClRoaXMgZGF0YXNldCBpcyBzZWFzb25hbGx5IGFkanVzdGVkIGJ1dCBpbiB0aGUgZm9sbG93aW5nIHBhcnQsIEkgdHJpZWQgdG8gZGVjb21wb3NlIHRoZSBGUEkgYW5kIGdldCB0cmVuZCBhbmQgc2Vhc29uYWwgY29tcG9uZW50cyB0b2dldGhlciB3aXRoIHRoZSByYW5kb20gKHJlc2lkdWFsKSBwYXJ0Lg0KDQojIyAqKjQuIERlY29tcG9zaXRpb24gb2YgQ1BJKioNCg0KYGBge3J9DQpkZWNvbXBvc2VkX2ZwaSA8LSB0c2xtKGZwaSB+IHRyZW5kICsgZm91cmllcihmcGksIDIpKQ0KDQp0cmVuZCA8LSBjb2VmKGRlY29tcG9zZWRfZnBpKVsxXSArIGNvZWYoZGVjb21wb3NlZF9mcGkpWyd0cmVuZCddKnNlcV9hbG9uZyhmcGkpDQoNCmNvbXBvbmVudHMgPC0gY2JpbmQoDQogIEZQSSA9IGZwaSwNCiAgdHJlbmQgPSB0cmVuZCwNCiAgc2Vhc29uYWwgPSBmcGkgLSB0cmVuZCAtIHJlc2lkdWFscyhkZWNvbXBvc2VkX2ZwaSksDQogIHJlc2lkdWFscyA9IHJlc2lkdWFscyhkZWNvbXBvc2VkX2ZwaSkpDQphdXRvcGxvdChjb21wb25lbnRzLCBmYWNldD1UUlVFKQ0KYGBgDQoNClRoZXJlIGFyZSA0IHN1YnBsb3RzIGluIHRoZSBhYm92ZSB2aXN1YWwgYW5kIHRoZSBmaXJzdCBvbmUgaXMgdGhlIHN1bSBvZiB0aGUgbGFzdCB0aHJlZSBtZWFuaW5nIHRoYXQgRlBJIGhhcyBhIHRyZW5kLCBhIHNlYXNvbmFsIGFuZCBhbiB1bmV4cGxhaW5lZCByZXNpZHVhbCBjb21wb25lbnQuIFRoaXMgaXMgaW50ZXJlc3RpbmcgYmVjYXVzZSBJIHdvdWxkIG5vdCBleHBlY3QgYW55IHNlYXNvbmFsIGNvbXBvbmVudCBzaW5jZSB0aGUgZGF0YSB3YXMgc2Vhc29uYWxseSBhZGp1c3RlZC4NCg0KQW5vdGhlciBpbnRlcmVzdGluZyByZXN1bHQgaXMgdGhhdCB3ZSBjYW5ub3Qgb25seSBleHBsYWluIHRoZSB2YXJpYXRpb24gaW4gdGhpcyB0aW1lIHNlcmllcyB3aXRoIHRyZW5kIGFuZCBzZWFzb25hbGl0eTogcmVzaWR1YWxzIHNlZW0gdG8gYmUgbm9uLXJhbmRvbSBhbmQgZm9yIHNvbWUgcGVyaW9kcywgdGhleSBhcHBlYXIgdG8gYmUgYXV0b2NvcnJlbGF0ZWQuDQoNCiMjIDUuIERpc3RyaWJ1dGlvbiBvZiBGb29kIFByaWNlIEluZGV4DQoNCiMjIyA1LjEuIFZpc3VhbHMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3QoZnBpLA0KICAgICBmcmVxID0gRkFMU0UsDQogICAgIG1haW4gPSAnSGlzdG9ncmFtIG9mIEZvb2QgUHJpY2UgSW5kZXgnLA0KICAgICB4bGFiID0gJ0Zvb2QgUHJpY2UgSW5kZXgnLA0KICAgICBjb2wgPSAnZGFya3JlZCcpDQpgYGANCg0KVGhlIGRpc3RyaWJ1dGlvbiBvZiBGUEkgKGFzIGlzKSBpcyBub3QgZXZlbiBjbG9zZSB0byBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCmhpc3QobG9nKGZwaSksDQogICAgIGZyZXEgPSBGQUxTRSwNCiAgICAgbWFpbiA9ICdIaXN0b2dyYW0gb2YgbG9nKEZvb2QgUHJpY2UgSW5kZXgpJywNCiAgICAgeGxhYiA9ICdsb2coRm9vZCBQcmljZSBJbmRleCknLA0KICAgICBjb2wgPSAnZGFya2dyZXknKQ0KYGBgDQoNCkFib3ZlIHZpc3VhbCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIG5hdHVyYWwgbG9nYXJ0aWhtIG9mIEZQSSBhbmQgZXZlbiB0aGUgbG9nIHRyYW5zZm9ybWVkIHZlcnNpb24gaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLg0KDQpgYGB7cn0NCmhpc3QoZGlmZihsb2coZnBpKSksDQogICAgIGZyZXEgPSBGQUxTRSwNCiAgICAgbWFpbiA9ICdIaXN0b2dyYW0gb2YgZGlmZihsb2coRm9vZCBQcmljZSBJbmRleCkpJywNCiAgICAgeGxhYiA9ICdkaWZmKGxvZyhGb29kIFByaWNlIEluZGV4KSknLA0KICAgICBjb2wgPSAnZGFya2dyZXknKQ0KYGBgDQoNCkluIGFkZGl0aW9uIHRvIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24sIHRoZSBtb250aGx5IGRpZmZlcmVuY2VzIGluIEZQSSB3ZXJlIGNhbGN1bGF0ZWQgYW5kIHBsb3R0ZWQgaW4gYWJvdmUgdmlzdWFsLiBUaGUgZGlzdHJpYnV0aW9uIHNoYXBlIG9mIHRoZSBsb2cgZGlmZmVyZW5jZWQgRlBJIGlzIGJldHRlciB0aGFuIHRoZSBmb3JtZXIgdHdvIHRyaWFscyBhbmQgdmVyeSBjbG9zZSB0byBub3JtYWwuDQoNCiMjIyA1LjIuIFN0YXRpc3RpY2FsIFRlc3RzIGZvciBOb3JtYWxpdHkNCg0KQWx0aG91Z2ggdmlzdWFscyBwbGF5IGdyZWF0IHJvbGUgaW4gdW5kZXJzdGFuZGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRpbWUgc2VyaWVzLCBmb3JtYWxseSBzdGF0aXN0aWNhbCB0ZXN0cyBhcmUgcmVxdWlyZWQgdG8gY2hlY2sgdGhlIG5vcm1hbGl0eSBvZiB0aGUgdmFyaWFibGVzLiBJbiB0aGUgZm9sbG93aW5nIHBhcnQsIEkgdXNlZCBKYXJxdWUtQmVyYSBUZXN0IHRvIHN0YXRpc3RpY2FsbHkgY2hlY2sgd2hldGhlciB0aGUgdGltZSBzZXJpZXMgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgb3Igbm90LiBJbiB0aGVzZSBzdGF0aXN0aWNhbCB0ZXN0cywgZm9sbG93aW5nIGh5cG90aGVzZXMgd2VyZSB1c2VkOg0KDQokJA0KXGJlZ2lue2FsaWduKn0gDQpIXzA6ICYgXHRleHR7VGhlIGRpc3RyaWJ1dGlvbiBvZiBGUEkgSVMgTk9UIHN0YXRpc3RpY2FsbHkgZGlmZmVyZW50IHRoYW4gbm9ybWFsIGRpc3RyaWJ1dGlvbn0gXFwNCkhfQTogJiBcdGV4dHtUaGUgZGlzdHJpYnV0aW9uIG9mIEZQSSBJUyBzdGF0aXN0aWNhbGx5IGRpZmZlcmVudCB0aGFuIG5vcm1hbCBkaXN0cmlidXRpb259DQpcZW5ke2FsaWduKn0NCiQkDQoNCmBgYHtyfQ0KamFycXVlLmJlcmEudGVzdChmcGkpDQpgYGANCg0KYGBge3J9DQpqYXJxdWUuYmVyYS50ZXN0KGxvZyhmcGkpKQ0KYGBgDQoNCmBgYHtyfQ0KamFycXVlLmJlcmEudGVzdChkaWZmKGxvZyhmcGkpKSkNCmBgYA0KDQpGUEksIGxvZyhGUEkpIGFuZCBkaWZmKGxvZyhGUEkpKSB3ZXJlIHRlc3RlZCBhbmQgYWxsIHRlc3RzIHJlc3VsdGVkIGluICRwLXZhbHVlIDwgXGFscGhhID0gMC4wNSQgbWVhbmluZyB0aGUgcmVqZWN0aW9uIG9mICRIXzAkLCB3aGljaCBzdGF0ZXMgbm9ybWFsIGRpc3RyaWJ1dGlvbiBvZiBGUEkuIFRoZXJlZm9yZSwgdGhlIGNvbmNsdXNpb24gaXMgdGhhdCAqKnRoZSB0aW1lIHNlcmllcyBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQqKi4NCg0KIyMgNi4gVGVzdGluZyBTdGF0aW9uYXJ5IChXaGl0ZSBOb2lzZSkNCg0KIyMjIDYuMS4gVmlzdWFscw0KDQojIyMjIDYuMS4xLiBMYWcgUGxvdCB0byBDaGVjayBBdXRvY29ycmVsYXRpb24NCg0KYGBge3J9DQpnZ2xhZ3Bsb3QoeCA9IGxvZyhmcGkpLCBsYWc9MTIsIHNlYXNvbmFsID0gVFJVRSkgKyANCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJBIikgKw0KICB0aGVtZV9idygpDQpgYGANCg0KVGhlIHNjYXR0ZXJwbG90IGdyaWQgcHJlc2VudGVkIGFib3ZlIGluZGljYXRlcyB0aGF0IGxvZyhGUEkpIGlzIHNpZ25pZmljYW50bHkgY29ycmVsYXRlZCB3aXRoIGl0cyBsYWdzLg0KDQpgYGB7cn0NCmdnbGFncGxvdCh4ID0gZGlmZihsb2coZnBpKSksIGxhZz0xMiwgc2Vhc29uYWwgPSBUUlVFKSArIA0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIkEiKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpJbnN0ZWFkIG9mIGxvZyhGUEkpLCBJIHVzZWQgZGlmZihsb2coRlBJKSkgdG8gY2hlY2sgZm9yIGF1dG9jb3JyZWxhdGlvbi4gVGhpcyB0aW1lLCB0aGUgbG9nIGRpZmZlcmVuY2VkIEZQSSBzaG93ZWQgbm8gc2lnbmlmaWNhbnQgYXV0b2NvcnJlbGF0aW9uIHdpdGggbGFnIHZhbHVlcy4NCg0KIyMjIyA2LjEuMi4gQXV0b2NvcnJlbGF0aW9uIGFuZCBQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiBQbG90cyAoQUNGICYgUEFDRikNCg0KYGBge3J9DQpmcGlfYWNmID0gZ2dBY2YobG9nKGZwaSksIGxhZy5tYXggPSA1MCwgcGxvdCA9IFRSVUUpICsgDQogIGxhYnModGl0bGUgPSAibG9nKEZQSSkgQUNGIikrDQogIHRoZW1lX2NsYXNzaWMoKQ0KZnBpX3BhY2YgPSBnZ1BhY2YobG9nKGZwaSksIGxhZy5tYXggPSA1MCwgcGxvdCA9IFRSVUUpICsNCiAgbGFicyh0aXRsZSA9ICJsb2coRlBJKSBQQUNGIikrDQogIHRoZW1lX2NsYXNzaWMoKQ0KZ3JpZC5hcnJhbmdlKGZwaV9hY2YsIGZwaV9wYWNmLCBucm93ID0gMSkNCmBgYA0KDQpBQ0YgYW5kIFBBQ0YgYXJlIGltcG9ydGFudCB2aXN1YWxzIHRvIGNoZWNrIHdoaWxlIGZpdHRpbmcgdGltZSBzZXJpZXMgbW9kZWxzLiBDb25zaWRlcmluZyB0aGUgcGxvdHRlZCBBQ0YgYW5kIFBBQ0YgcGxvdHRlZCBhYm92ZSwgSSBjYW4gc3RhdGUgdGhhdCBsb2coRlBJKSBmb2xsb3dzIGEgTUEgcHJvY2VzcyBpbnN0ZWFkIG9mIGFuIEFSIHByb2Nlc3Mgc2luY2UgYmFycyBpbiBBQ0Ygc2xpZ2h0bHkgZGVjcmVhc2VzIHdpdGggaW5jcmVhc2luZyBsYWdzIGJ1dCBiYXJzIGluIFBBQ0YgZGlyZWN0bHkgYmVjb21lcyBpbnNpZ25pZmljYW50IGFmdGVyIGZpcnN0IG9uZS4NCg0KIyMjIDYuMi4gU3RhdGlzdGljYWwgVGVzdHMgZm9yIFN0YXRpb25hcnkgKFdoaXRlIE5vaXNlKQ0KDQpTaW1pbGFyIHRvIHRoZSBub3JtYWxpdHkgdGVzdCwgaW4gYWRkaXRpb24gdG8gdGhlIHZpc3VhbHMsIEkgdXNlZCBzdGF0aXN0aWNhbCB0ZXN0IHRvIGRldGVjdCBzdGF0aW9uYXJ5ICh1bml0IHJvb3QpLiBGb2xsb3dpbmcgaHlwb3RoZXNlcyB3ZXJlIHVzZWQgaW4gdGhlc2UgdGVzdHMuDQoNCiQkDQpcYmVnaW57YWxpZ24qfSANCkhfMDogJiBcdGV4dHtOb24gU3RhdGlvbmFyeSAoVW5pdCBSb290IHByZXNlbnQpfSBcXA0KSF9BOiAmIFx0ZXh0e1N0YXRpb25hcnkgKFVuaXQgUm9vdCBOT1QgcHJlc2VudCl9DQpcZW5ke2FsaWduKn0NCiQkDQoNCmBgYHtyfQ0KYWRmLnRlc3QobG9nKGZwaSksIGFsdGVybmF0aXZlID0gJ3N0YXRpb25hcnknKQ0KYGBgDQoNCmBgYHtyfQ0KYWRmLnRlc3QoZGlmZihsb2coZnBpKSksIGFsdGVybmF0aXZlID0gJ3N0YXRpb25hcnknKQ0KYGBgDQoNClN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0cyBzdXBwb3J0cyB0aGUgdmlzdWFscyBkaXNjdXNzZWQgaW4gdGhlIHByZXZpb3VzIHBhcnQ6DQoNCi0gICBGb3IgbG9nKEZQSSksIHRoZSBjYWxjdWxhdGVkICRwLXZhbHVlID4gXGFscGhhID0gMC4wNSQgbWVhbmluZyB0aGF0ICRIXzAkIGNhbm5vdCBiZSByZWplY3RlZC4gVGhlIGNvbmNsdXNpb24gZm9yIGxvZyhGUEkpIGlzIHRoYXQgaXQgaGFzIHVuaXQgcm9vdCAob3IgaXQgaXMgbm90IHN0YXRpb25hcnkpLiBUaGlzIGlzIG5vdCBzdXJwcmlzaW5nIHNpbmNlIHRoZSBvcmlnaW5hbCBGUEkgZGF0YSBoYWQgYSB0cmVuZCBjb21wb25lbnQgKihyZWZlciB0byBkZWNvbXBvc2l0aW9uIHBhcnQgZGlzY3Vzc2VkIGFib3ZlKS4qDQoNCi0gICBGb3IgbG9nIGRpZmZlcmVuY2VkIEZQSSwgbXkgY29uY2x1c2lvbiBpcyB0aGUgb3Bwb3NpdGUgc2luY2UgdGhlIGNhbGN1bGF0ZWQgJHAtdmFsdWUgPCBcYWxwaGEgPSAwLjA1JCBtZWFuaW5nIHRoYXQgJEhfMCQgbXVzdCBiZSBiZSByZWplY3RlZC4gVGhlIGNvbmNsdXNpb24gZm9yIGRpZmYobG9nKEZQSSkpIGlzIHRoYXQgaXQgZG9lcyBub3QgaGF2ZSB1bml0IHJvb3QgKG9yIGl0IGlzIHN0YXRpb25hcnkpLg0KDQpJbiBhYm92ZSBwYXJ0LCBJIGRpc2N1c3NlZCBhYm91dCBGUEkgKGFuZCBsb2coRlBJKSkgZm9sbG93IGEgTUEgcHJvY2VzcyBpbnN0ZWFkIG9mIGFuIEFSIHByb2Nlc3MuIEluIGFkZGl0aW9uIHRvIHRoYXQsIHN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0cyBkaXNjdXNzZWQgaW4gdGhpcyBwYXJ0IGFsc28gc3VnZ2VzdCB0aGF0IHRoZXJlIHdpbGwgYmUgYW4gSSBjb21wb25lbnQgaW4gdGhlIEFSSU1BIHByb2Nlc3MgYmVjYXVzZSB3aXRoIHRoZSBpbnRlZ3JhdGVkIGRpZmZlcmVuY2luZywgZGF0YSBiZWNvbWUgc3RhdGlvbmFyeS4NCg0KIyMgNy4gQVJJTUEgKG9yIFNBUklNQSkgTW9kZWwNCg0KIyMjIDcuMS4gRml0dGluZyB0aGUgbW9kZWwNCg0KQSBnZW5lcmFsIEFSSU1BIG1vZGVsIHJlcXVpcmVzIHRocmVlIHBhcmFtZXRlcnM6DQoNCi0gICBwIGZvciBBUiBjb21wb25lbnQ6IHAgcmVmZXJzIHRvIG51bWJlciBvZiBsYWcgdmFsdWVzIHRvIGluY2x1ZGUgaW4gdGhlIG1vZGVsDQoNCi0gICBkIGZvciBJIGNvbXBvbmVudDogZCByZWZlcnMgdG8gZGlmZmVyZW5jaW5nIHdpbmRvdw0KDQotICAgcSBmb3IgTUEgY29tcG9uZW50OiBxIHJlZmVycyB0byB0aGUgbnVtYmVyIG9mIHBhc3QgcmVzaWR1YWxzIHRvIGluY2x1ZGUgaW4gdGhlIG1vZGVsLg0KDQpSZW1lbWJlciwgaW4gdGhlIGRlY29tcG9zaXRpb24gcGFydCwgSSBkaXNjdXNzZWQgYWJvdXQgdGhlIHN1cnByaXNpbmcgc2Vhc29uYWwgY29tcG9uZW50IG9mIEZQSS4gVGhlcmVmb3JlLCBteSBleHBlY3RhdGlvbiBpcyB0aGF0IHRoZSBmaXR0ZWQgbW9kZWwgd2lsbCBiZSBTQVJJTUEgKHNlYXNvbmFsIEFSSU1BKSwgd2hpY2ggcmVxdWlyZXMgdGhyZWUgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIChQLEQsUSBzYW1lIG1lYW5pbmcgd2l0aCBhYm92ZSBwYXJhbWV0ZXJzIGJ1dCB0aGlzIHRpbWUgc2Vhc29uYWwpLg0KDQpUaGUgb3B0aW1hbCBwYXJhbWV0ZXJzIGNhbiBiZSBkZWNpZGVkIHdpdGggdHJpYWxzIGFuZCB0aGVuIGNvbXBhcmluZyB0aGUgQUlDIG9yIEJJQyB2YWx1ZXMgKHRoZSBsb3dlc3Qgc2hvdWxkIGJlIGNob3Nlbikgb2YgdGhlIG1vZGVscy4gSG93ZXZlciwgYXV0by5hcmltYSgpIGZ1bmN0aW9uIGV4YWN0bHkgZG9lcyB0aGUgc2FtZSB0aGluZyBhbmQgdGhlcmVmb3JlIEkgcHJlZmVyIHRvIHVzZSBpdCB0byBiZSBwcmFjdGljYWwuDQoNCioqKk5vdGU6KiogU2luY2UgZGlmZmVyZW5jaW5nIGNhbiBiZSBoYW5kbGVkIHdpdGhpbiB0aGUgQVJJTUEgKG9yIFNBUklNQSkgcHJvY2VzcyBieSBzZXR0aW5nIGRcPjAsIEkgdXNlZCBsb2coRlBJKSBpbnNpZGUgdGhlIGZ1bmN0aW9uLiBhdXRvLmFyaW1hIHdpbGwgY2hvb3NlIGEgbm9uLXplcm8gdmFsdWUgZm9yIGQgdG8gbWFuYWdlIGRpZmZlcmVuY2luZy4qDQoNCmBgYHtyfQ0KQVJJTUFfZnBpID0gYXV0by5hcmltYShsb2coZnBpKSkNCnN1bW1hcnkoQVJJTUFfZnBpKQ0KYGBgDQoNClNpbWlsYXIgdG8gbXkgZGlzY3Vzc2lvbiBpbiBmb3JtZXIgc2VjdGlvbnMgb2YgdGhpcyBwYXBlciwgYXV0by5hcmltYSgpIGZ1bmN0aW9uIHJlc3VsdGVkIGluIGEgU0FSSU1BKDAsMiwxKSgwLDAsMSkgbW9kZWwuIEFzIGRpc2N1c3NlZCBhYm92ZSwNCg0KLSAgIFRoZXJlIGlzIG5vIEFSIGNvbXBvbmVudA0KDQotICAgVGhlcmUgaXMgSSBjb21wb25lbnQgd2l0aCBkID0gMg0KDQotICAgVGhlcmUgaXMgTUEgY29tcG9uZW50IHdpdGggcSA9IDENCg0KLSAgIFRoZXJlIGlzIHNlYXNvbmFsIE1BIHdpdGggUSA9IDENCg0KIyMgOC4gTW9kZWwgRGlhZ25vc3RpY3MNCg0KYGBge3J9DQp0c2RpYWcoQVJJTUFfZnBpKQ0KYGBgDQoNClRoZSByZXNpZHVhbCBwbG90IG9mIHRoZSBmaXR0ZWQgbW9kZWwgaW5kaWNhdGVzIHNpZ25zIG9mIG5vbi1jb25zdGFudCB2YXJpYW5jZSBzaW5jZSB0aGVyZSBhcmUgcGlja3MgaW4gdGhlIHJlc2lkdWFscyBpbiBzb21lIHBlcmlvZHMgY29tcGFyZWQgdG8gb3RoZXIgcGVyaW9kcy4gVGhpcyBzaG93cyB0aGUgdmFyaWF0aW9uIGluIHRoZSByZXNpZHVhbHMgYXJlIG5vdCBjb25zdGFudC4NCg0KYGBge3J9DQpjaGVja3Jlc2lkdWFscyhBUklNQV9mcGksIHRlc3QgPSBGQUxTRSkNCmBgYA0KDQpUaGUgQUNGIG9mIHJlc2lkdWFscyBsb29rIGFjY2VwdGFibGUgYW5kIHRoZSBkaXN0cmlidXRpb24gb2YgcmVzaWR1YWxzIGlzIGNsb3NlIHRvIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KYXV0b3Bsb3QoQVJJTUFfZnBpKQ0KYGBgDQoNCkFsbCB0aGUgZG90cyBhcmUgaW5zaWRlIHRoZSBjaXJjbGUsIHdoaWNoIGlzIGFuIGluZGljYXRpb24gb2YgYW4gYWNjZXB0YWJsZSBtb2RlbC4NCg0KYGBge3J9DQphcmNoVGVzdChyZXNpZHVhbHMoQVJJTUFfZnBpKSkNCmBgYA0KDQpJbiBvcmRlciB0byB0ZXN0IGZvciBoZXRlcm9zY2VkYXN0aWNpdHksIEkgdXNlZCBhcmNoVGVzdCBmcm9tIE1UUyBwYWNrYWdlICh3aGljaCB1c2VzIExqdW5nLUJveCB0ZXN0KS4gVGhlICRwLXZhbHVlJCBvZiB0aGUgcmFuay1iYXNlZCB0ZXN0IGlzIGdyZWF0ZXIgdGhhbiAkXGFscGhhID0gMC4wNSQgYnV0IHRoZSBub3JtYWwgdGVzdCBoYXMgYSAkcC12YWx1ZSA8IFxhbHBoYSA9IDAuMDUkLlwNCiQkDQpcYmVnaW57YWxpZ24qfSANCkhfMDogJiBcdGV4dHtObyBhdXRvY29ycmVsYXRpb24gb2YgcmVzaWR1YWwgc3F1YXJlcyAoUmVzaWR1YWxzIGFyZSByYW5kb20pfSBcXA0KSF9BOiAmIFx0ZXh0e0F1dG9jb3JyZWxhdGlvbiBvZiByZXNpZHVhbCBzcXVhcmVzIChSZXNpZHVhbHMgYXJlIE5PVCByYW5kb20pfQ0KXGVuZHthbGlnbip9DQokJA0KDQpBY2NvcmRpbmcgdG8gdGhlIHBhY2thZ2UgZGVmaW5pdGlvbiBmb3IgYXJjaFRlc3QoKSwgdGhlIHJhbmsgc2VyaWVzIG9mIHRoZSBzcXVhcmVkIHRpbWUgc2VyaWVzIGlzIHRoYW4gdXNlZCB0byB0ZXN0IHRoZSBjb25kaXRpb25hbCBoZXRlcm9zY2VkYXN0aWNpdHkuIE91ciBtb2RlbCBpcyBzdWNjZXNzZnVsIGluIHRlcm1zIG9mIHJhbmstYmFzZWQgdGVzdCBtb2RlbCBhbmQgdGhlcmVmb3JlLCB3ZSBjYW4gbWFrZSBmb3JlY2FzdHMgYmFzZWQgb24gdGhlIGV4aXN0aW5nIG1vZGVsLiBIb3dldmVyLCB0aGUgcC12YWx1ZXMgYXJlIHZlcnkgY2xvc2UgdG8gdGhlIGNyaXRpY2FsIGxldmVscyBhbmQgdGhlcmVmb3JlLCB0aGVyZSBpcyBhIG5lZWQgZm9yIEFSQ0ggb3IgR0FSQ0ggbW9kZWxzIHRvIHByZWRpY3QgdGhlIHZhcmlhbmNlLg0KDQojIEZvcmVjYXN0aW5nDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpmcGlfZm9yZWNhc3QgPSBmb3JlY2FzdChBUklNQV9mcGksIGggPSAxMiwgbGV2ZWwgPSBjKDk1LDk5KSkNCg0KZnBpX2ZvcmVjYXN0JHggPSBleHAoZnBpX2ZvcmVjYXN0JHgpDQpmcGlfZm9yZWNhc3QkbWVhbiA9IGV4cChmcGlfZm9yZWNhc3QkbWVhbikNCmZwaV9mb3JlY2FzdCRsb3dlciA9IGV4cChmcGlfZm9yZWNhc3QkbG93ZXIpDQpmcGlfZm9yZWNhc3QkdXBwZXIgPSBleHAoZnBpX2ZvcmVjYXN0JHVwcGVyKQ0KDQojIHVzaW5nIHBsb3RseSBpbiBSIHRvIHBsb3QgaW50ZXJhY3RpdmUgdmlzdWFscy4gDQpwbG90X2ZvcmVjYXN0KGZwaV9mb3JlY2FzdCwgDQogICAgICAgICAgICAgIHRpdGxlID0gJ0Zvb2QgUHJpY2UgSW5kZXggRm9yZWNhc3QgZm9yIDIwMjMnLCANCiAgICAgICAgICAgICAgWHRpdGxlID0gJ1RpbWUnLCANCiAgICAgICAgICAgICAgWXRpdGxlID0gJ0Zvb2QgUHJpY2UgSW5kZXgnLCANCiAgICAgICAgICAgICAgY29sb3IgPSBOVUxMLCANCiAgICAgICAgICAgICAgd2lkdGggPSAyKQ0KYGBgDQoNCiMgQ29uY2x1c2lvbg0KDQpDYW5hZGEncyBDUEkgZm9yIGZvb2QgKG9yIEZQSSkgZm9sbG93cyBhIHNlYXNvbmFsIEFSSU1BIHByb2Nlc3MuIEZvbGxvd2luZyB0aGUgdGVzdHMgaW1wbGVtZW50ZWQgdG8gZW5zdXJlIG1lZXRpbmcgdGhlIGFzc3VtcHRpb25zLCBJIGZpdHRlZCBhIHNlYXNvbmFsIEFSSU1BIG1vZGVsIHRvIGZvcmVjYXN0IEZQSSB2YWx1ZXMgZm9yIDIwMjMuIE15IGZvcmVjYXN0cyBpbmRpY2F0ZXMgdGhhdCBmb29kIHByaWNlcyBpbiBDYW5hZGEgYXJlIGxpa2VseSB0byBpbmNyZWFzZSBmdXJ0aGVyIGluIDIwMjMuIEZ1cnRoZXJtb3JlLCBJIHBsYW4gdG8gZml0IGFuIEFSQ0ggb3IgR0FSQ0ggbW9kZWwgZm9yIHRoZSBjb25kaXRpb25hbCBoZXRlcm9zY2VkYXN0aWNpdHkgYXMgdGhlIG5leHQgc3RlcCBvZiB0aGlzIHBhcGVyLg0KDQojIFJlZmVyZW5jZXMNCg0KUm9zZXIsIE1heCwgYW5kIEhhbm5haCBSaXRjaGllLiAoMjAyMSkgKkZvb2QgUHJpY2VzKi4gKk91ciBXb3JsZCBpbiBEYXRhKiwgKm91cndvcmxkaW5kYXRhLm9yZyouIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vb3Vyd29ybGRpbmRhdGEub3JnL2Zvb2QtcHJpY2VzLj4gKEFjY2Vzc2VkOiBKYW51YXJ5IDE4LCAyMDIzKS4NCg0KQm9nbWFucywgQy4sIFBlc2NhdG9yaSwgQS4gYW5kIFByaWZ0aSwgRS4gKDIwMjEpICpGb3VyIGZhY3RzIGFib3V0IHNvYXJpbmcgY29uc3VtZXIgZm9vZCBwcmljZXMqLCAqSU1GKi4gQXZhaWxhYmxlIGF0OiA8aHR0cHM6Ly93d3cuaW1mLm9yZy9lbi9CbG9ncy9BcnRpY2xlcy8yMDIxLzA2LzI0L2ZvdXItZmFjdHMtYWJvdXQtc29hcmluZy1jb25zdW1lci1mb29kLXByaWNlcz4gKEFjY2Vzc2VkOiBKYW51YXJ5IDE4LCAyMDIzKS4NCg0KRnJhZGVsbGEsIEEuICgyMDIyKSAqQmVoaW5kIHRoZSBOdW1iZXJzOiBXaGF0J3MgQ2F1c2luZyBHcm93dGggaW4gRm9vZCBQcmljZXMsKiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3MTUwLnN0YXRjYW4uZ2MuY2EvbjEvcHViLzYyZjAwMTRtLzYyZjAwMTRtMjAyMjAxNC1lbmcuaHRtPiAoQWNjZXNzZWQ6IEphbnVhcnkgMTgsIDIwMjMpLg0KDQpTdGF0aXN0aWNzIENhbmFkYSAoMjAyMykgKkNvbnN1bWVyIHByaWNlIGluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKiwgKkNvbnN1bWVyIFByaWNlIEluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKi4gR292ZXJubWVudCBvZiBDYW5hZGEsIFN0YXRpc3RpY3MgQ2FuYWRhLiBBdmFpbGFibGUgYXQ6IDxodHRwczovL3d3dzE1MC5zdGF0Y2FuLmdjLmNhL3QxL3RibDEvZW4vdHYuYWN0aW9uP3BpZD0xODEwMDAwNjAxPiAoQWNjZXNzZWQ6IEphbnVhcnkgMTgsIDIwMjMpLg0KDQpTdGF0aXN0aWNzIENhbmFkYSAoMjAyMikgKlN0YXRpc3RpY3MgQ2FuYWRhIE9wZW4gTGljZW5jZSosICpHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEqLiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LnN0YXRjYW4uZ2MuY2EvZW4vcmVmZXJlbmNlL2xpY2VuY2U+IChBY2Nlc3NlZDogSmFudWFyeSAxOCwgMjAyMykuDQoNClN0YXRpc3RpY3MgQ2FuYWRhICgyMDIyKSAqV2ViIGRhdGEgc2VydmljZSAoV0RTKSosICpHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEqLiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LnN0YXRjYW4uZ2MuY2EvZW4vZGV2ZWxvcGVycy93ZHM+IChBY2Nlc3NlZDogSmFudWFyeSAxOCwgMjAyMykuDQo=